/* TCSS 590A - Cybernetics and Systems
 * Group Project - Modeling
 * By Barry Jones, Naren Meadem, Michael Pitts, Yuan Tao
 * Fall 2012, University of Washington, Tacoma
 */
package model.delayed;

import java.util.ArrayList;
import java.util.Collection;
import java.util.PriorityQueue;
import java.util.Queue;

import model.Model;

import output.OutPut;

/**
 * A income, expenses, and interest model where financial transfers are delayed.
 * @author Michael Pitts
 * @version Nov 20, 2012
 */
public class DelayedModel implements Model
{
	
	/** Generates wage or income Events, Events which add positive value.*/
	private final Generator my_wage;
	
	/** Generates cost or expense Events, Events which have negative value.*/
	private final Generator my_costs;
	
	/** All of the accounts for this Model.  The first is the primary spending
	 * and receiving account, or checking.*/
	private final Account[] my_accounts;
	
	/** Controls the reactions of the Model, logistical, tactical, and strategic.*/
	private final ModelController my_control;
	
	/** Stores all Events, with the soonest ones first.*/
	private final Queue<Event> my_events;
	
	/** The maximum number of iterations for this Model.*/
	private final long my_max;
	
	/** The output for this Model.*/
	private final OutPut my_out;
	
	/** The current iteration of this Model.*/
	private long my_iterations;
	
	/**
	 * Generates a new Delayed Model.
	 * @param the_accounts are the accounts for this Model.
	 * @param the_wage is wage Generator.
	 * @param the_costs is the costs Generator.
	 * @param the_max is the maximum number of iterations to run.
	 * @param the_out is the OutPut object.
	 */
	public DelayedModel(final Account[] the_accounts,
			final Generator the_wage, final Generator the_costs, 
			final ModelController the_control, final long the_max, 
			final OutPut the_out)
	{
		my_accounts = the_accounts;
		my_events = new PriorityQueue<Event>();
		my_iterations = 0;
		my_max = the_max;
		my_out = the_out;
		my_wage = the_wage;
		my_costs = the_costs;
		my_control = the_control;
	}
	
	/** Outputs the current state of the model.*/
	private void outState()
	{
		my_out.out(my_iterations);
		for(int i = 0; i < my_accounts.length; i++)
		{
			my_out.delineate();
			my_out.out(my_accounts[i].getValue());
		}
		
		my_out.newLine();
	}

	/**
	 * Checks if the conditions have been meet to stop the simulation.
	 * @return true if to continue, or false if the simulation should stop.
	 */
	private boolean checkConditions()
	{
		outState();
		boolean go = my_iterations < my_max;
		if (go) 
			for (Account ac : my_accounts)
			{
				go &= ac.inLegalState();
				if (!ac.inLegalState()) 
				{
					my_out.out("Illegal state: " + ac.getClass().getName());
					my_out.newLine();
				}
		}
		if (!go) my_out.close();
		return go;
	}

	/**
	 * Executes all the events for this time period.
	 */
	private void executeEvents()
	{
		Event event;
		while(my_events.peek() != null && my_events.peek().time == my_iterations)
		{
			event = my_events.remove();
			if (event.account == null)
				my_accounts[0].changeValue(event.value);
			else
				event.account.changeValue(event.value);
		}
	}
	
	
	@Override public boolean runOne()
	{
		my_iterations++;
		final Collection<Event> events = new ArrayList<Event>();
		Event temp = my_wage.getEvent(my_iterations);
		while (temp != null) 
		{
			events.add(temp);
			temp = my_wage.getEvent(my_iterations);
		}
		temp = my_costs.getEvent(my_iterations);
		while (temp != null)
		{
			events.add(temp);
			temp = my_costs.getEvent(my_iterations);
		}
		my_events.addAll(my_control.compute(my_iterations, events));
		executeEvents();
		for(int i = 0; i < my_accounts.length; i++)my_accounts[i].addInterest();
		return checkConditions();
	}
}
